package com.wang.jmonkey.gateway.handler.auth.converter;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description 解析请求的Header头信息，如果请求的url带有access_token信息则强制将access_token的参数添加到Header中。如果header中Authorization的值不带有Bearer则强制增加该信息.
 * @Author HeJiawang
 * @Date 2021/5/31 16:06
 */
public class TokenAuthenticationConverter implements ServerAuthenticationConverter {

    private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+)=*$");

    private boolean allowUriQueryParameter = false;

    public Mono<Authentication> convert(ServerWebExchange exchange) {
        return Mono.justOrEmpty(this.token(exchange.getRequest()))
                .map(BearerTokenAuthenticationToken::new);
    }

    /**
     * 解析token信息
     * @param request
     * @return
     */
    private String token(ServerHttpRequest request) {

        String authorizationHeaderToken = resolveFromAuthorizationHeader(request.getHeaders());
        String parameterToken = request.getQueryParams().getFirst("access_token");
        if (authorizationHeaderToken != null) {
            if (parameterToken != null) {
                if(!authorizationHeaderToken.equals(parameterToken)){
                    BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST,
                            HttpStatus.BAD_REQUEST,
                            "Found multiple bearer tokens in the request",
                            "https://tools.ietf.org/html/rfc6750#section-3.1");
                    throw new OAuth2AuthenticationException(error);
                }
            }
            return authorizationHeaderToken;
        }
        //请求参数支持
        else if (parameterToken != null && isParameterTokenSupportedForRequest(request)) {
            return parameterToken;
        }
        return null;
    }

    public void setAllowUriQueryParameter(boolean allowUriQueryParameter) {
        this.allowUriQueryParameter = allowUriQueryParameter;
    }

    /**
     * 判断header的token信息是否是Bearer开头。如果格式符合"^Bearer (?<token>[a-zA-Z0-9-._~+/]+)=*$"则提取token数据.
     * @param headers
     * @return
     */
    private static String resolveFromAuthorizationHeader(HttpHeaders headers) {
        String authorization = headers.getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.hasText(authorization) && authorization.startsWith("Bearer")) {
            Matcher matcher = authorizationPattern.matcher(authorization);

            if ( !matcher.matches() ) {
                BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN,
                        HttpStatus.BAD_REQUEST,
                        "Bearer token is malformed",
                        "https://tools.ietf.org/html/rfc6750#section-3.1");
                throw new OAuth2AuthenticationException(error);
            }

            return matcher.group("token");
        }
        return null;
    }

    private boolean isParameterTokenSupportedForRequest(ServerHttpRequest request) {
        return this.allowUriQueryParameter && HttpMethod.GET.equals(request.getMethod());
    }
}
